package com.tanhua.server.service;

import cn.hutool.core.date.DateUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.constants.Constants;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.dubbo.api.db.BlackListApi;
import com.tanhua.dubbo.api.db.UserInfoApi;
import com.tanhua.dubbo.api.mongo.FriendApi;
import com.tanhua.dubbo.api.mongo.SoundApi;
import com.tanhua.model.db.BlackList;
import com.tanhua.model.db.UserInfo;
import com.tanhua.model.mongo.Friend;
import com.tanhua.model.mongo.Sound;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.SoundVo;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.io.IOException;
import java.util.Date;

/**
 * 桃花传音业务层
 *
 * @author youshengfeng
 * @date 2022/2/20 上午 10:54
 */
@Service
@Slf4j
public class PeachblossomService {


    @Autowired//注入redis
    private RedisTemplate redisTemplate;

    @DubboReference
    private UserInfoApi userInfoApi;
    @DubboReference
    private SoundApi soundApi;
    @DubboReference
    private BlackListApi blackListApi;
    @DubboReference
    private FriendApi friendApi;
    @Autowired
    private FastFileStorageClient client;
    @Autowired
    private FdfsWebServer webServer;


    /**
     * 发送语音
     *
     * @param soundFile 语音文件
     */
    public void addVoice(MultipartFile soundFile) {
        // 获取userId
        Long userId = UserHolder.getUserId();
        // 大key VoiceCount_UserId_Date
        Date today = new Date();
        String yyyyMMdd = DateUtil.format(today, "yyyyMMdd");
        String redisKey = Constants.SOUND_COUNT_KEY + userId + "_" + yyyyMMdd;
        // 1.查询redis中有没有该用户的语音数据
        // 1.1获取发送语音对应的value
        Integer redisValue = (Integer) redisTemplate.opsForHash().get(redisKey, "send_Sound");
        // 1.2如果redis没有发送次数的value 就设置发送次数的小key
        if (redisValue == null) {
            // 1.2.1 redis设置该用户的发送剩余次数为3次
            redisTemplate.opsForHash().put(redisKey, "send_Sound", 3);
            redisValue = 3;
            // 1.2.2如果接收语音的小key不存在 那么就是没有生命周期 所以需要增加生命周期
            if (redisTemplate.opsForHash().get(redisKey, "reception_Sound") == null) {
                redisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
            }
        }
        // 1.3 如果redis保存次数不合格,就抛出异常
        else if (redisValue < 1 || redisValue > 3) {
            // 如果为0就提示用户次数已用完，抛出异常告知用户
            throw new TanHuaException("次数已用完，请明天再发表语音");
        }
        // 2.构造sound对象
        // 2.1 获取url
        String url = null;
        try {
            // 2.1.2将文件传入云端获取url
            String soundName = soundFile.getOriginalFilename();
            String suffix = soundName.substring(soundName.lastIndexOf(".") + 1);
            StorePath storePath = client.uploadFile(soundFile.getInputStream(), soundFile.getSize(), suffix, null);
            url = webServer.getWebServerUrl() + storePath.getFullPath();
        } catch (IOException e) {
            throw new TanHuaException("网络异常,语音发送失败");
        }
        // 2.2 从userinfo表获取性别
        UserInfo userInfo = userInfoApi.findUserInfo(userId);
        String gender = userInfo.getGender();//用户性别
        // 2.3 构建一个sound对象设置参数
        Sound sound = new Sound();
        sound.setUserId(userId);
        sound.setSoundUrl(url);
        sound.setGender(gender);
        sound.setCreated(System.currentTimeMillis());
        //3. 远程调用 存入数据库
        soundApi.addVoice(sound);
        //4. 将redis的剩余次数减一
        redisTemplate.opsForHash().put(redisKey, "send_Sound", redisValue - 1);
    }

    /**
     * 接收语音
     *
     * @return
     */
    public SoundVo receptionVoices() {
        Long userId = UserHolder.getUserId();
        Date today = new Date();
        String yyyyMMdd = DateUtil.format(today, "yyyyMMdd");
        String redisKey = Constants.SOUND_COUNT_KEY + userId + "_" + yyyyMMdd;
        Integer redisValue = (Integer) redisTemplate.opsForHash().get(redisKey, "reception_Sound");
        if (redisValue == null) {
            // 1.2.1 redis设置该用户的发送接收次数为3次
            redisTemplate.opsForHash().put(redisKey, "reception_Sound", 3);
            redisValue = 3;
            // 1.2.2如果发送语音的小key不存在 那么就是没有生命周期 所以需要增加生命周期
            if (redisTemplate.opsForHash().get(redisKey, "send_Sound") == null) {
                redisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
            }
        }

        // 1.3 如果redis保存次数不合格,就抛出异常
        else if (redisValue < 1 || redisValue > 3) {
            // 如果为0就提示用户次数已用完，抛出异常告知用户
            throw new TanHuaException("次数已用完，请明天再发表语音");
        }
        //2.构造Vo返回
        //2.1获得推荐的sound(调用方法)
        Sound sound = getSound(userId);
        //2,2定义一个Vo
        SoundVo soundVo = new SoundVo();
        //封装vo
        //設置語音用戶的id
        soundVo.setId(sound.getUserId());
        //设置语音url
        soundVo.setSoundUrl(sound.getSoundUrl());
        //性别
        soundVo.setGender(sound.getGender());
        //查询userInfo表
        UserInfo userInfo = userInfoApi.findUserInfo(sound.getUserId());
        //年龄
        soundVo.setAge(userInfo.getAge());
        //头像
        soundVo.setAvatar(userInfo.getAvatar());
        //昵称
        soundVo.setNickname(userInfo.getNickname());
        //剩余次数
        soundVo.setRemainingTimes(redisValue);
        redisTemplate.opsForHash().put(redisKey, "reception_Sound", redisValue - 1);
        return soundVo;
        /*

        if (redisValue == null) {
            //说明用户今天没有接收语音
            Sound sound = getSound(userId);
            SoundVo soundVo = new SoundVo();
            BeanUtils.copyProperties(sound, soundVo);
            UserInfo userInfo = userInfoApi.findUserInfo(sound.getUserId());
            //設置語音用戶的id
            soundVo.setId(sound.getUserId());
            //年龄
            soundVo.setAge(userInfo.getAge());
            //头像
            soundVo.setAvatar(userInfo.getAvatar());
            //昵称
            soundVo.setNickname(userInfo.getNickname());
            //剩余次数
            soundVo.setRemainingTimes(3);
            redisTemplate.opsForHash().put(redisKey, "reception_Sound", 3);
            //如果接收语音的小key不存在 那么就是没有生命周期 所以需要增加生命周期
            if (redisTemplate.opsForHash().get(redisKey, "send_Sound") == null) {
                redisTemplate.expire(redisKey, 1, TimeUnit.DAYS);
            }
            return soundVo;
        } else {
            if (redisValue > 1) {
                Sound sound = getSound(userId);
                SoundVo soundVo = new SoundVo();
                BeanUtils.copyProperties(sound, soundVo);
                UserInfo userInfo = userInfoApi.findUserInfo(sound.getUserId());
                //設置語音用戶的id
                soundVo.setId(sound.getUserId());
                //年龄
                soundVo.setAge(userInfo.getAge());
                //头像
                soundVo.setAvatar(userInfo.getAvatar());
                //昵称
                soundVo.setNickname(userInfo.getNickname());
                //剩余次数
                soundVo.setRemainingTimes(redisValue - 1);
                redisTemplate.opsForHash().put(redisKey, "reception_Sound", redisValue - 1);
                return soundVo;
            } else {
                throw new TanHuaException("今日获取语音的次数不足");
            }

         */
    }


    /**
     * 获得给用户推荐语音
     *
     * @param userId
     * @return
     */
    private Sound getSound(long userId) {
        //        1.查询当前用户的性别(userinfo)
        UserInfo userInfo = userInfoApi.findUserInfo(userId);
        String gender = userInfo.getGender();
 /*
        //对性别进行取反
        String findGender = "";
        if (gender.equals("man")) {
            findGender = "woman";
        } else {
            findGender = "man";
        }
*/
        //定义一个集合 封装所有屏蔽的用户id
        List<Long> noFindIds = new ArrayList<>();

        //查询用户的好友列表
        List<Friend> friends = friendApi.findFriendList(userId);
        List<Long> friendIds = friends.stream().map(Friend::getFriendId).collect(Collectors.toList());
        //将好友id集合封装入屏蔽id集合
        if (friendIds.size() > 0) {
            noFindIds.addAll(friendIds);
        }
        //查询用户的黑名单列表
        List<BlackList> blacklist = blackListApi.findBlacklist(userId);
        if (blacklist != null && blacklist.size() > 0) {
            for (BlackList blackList : blacklist) {
                Long blackUserId = blackList.getBlackUserId();
                //将黑名单id封装入屏蔽id集合
                noFindIds.add(blackUserId);
            }
        }
        //List<Long> blackUserIds = blacklist.stream().map(BlackList::getUserId).collect(Collectors.toList());

        /*
        //查询用户 不喜欢列表
        String notLikeUserkey = Constants.USER_NOT_LIKE_KEY + userId;
        Set members = redisTemplate.opsForSet().members(notLikeUserkey);
        if (members != null&&members.size()>0) {
            for (Object member : members) {
                long l = Long.parseLong(member.toString());
                //将不喜欢id封装入屏蔽id集合
                noFindIds.add(l);
            }
        }
        */
        //将自己的id封装入屏蔽id集合
        noFindIds.add(userId);
        //查询用户 不喜欢列表  得到用户不喜欢的用户id集合
        String notLikeUserkey = Constants.USER_NOT_LIKE_KEY + userId;
        Set<Long> notLikeUserIds = redisTemplate.opsForSet().members(notLikeUserkey);
        //获得用户 已经听过的语音的 objectId
        String readVoiceKey = Constants.readVoice_USER_KEY + userId;
        Set<ObjectId> readVoiceIds = redisTemplate.opsForSet().members(readVoiceKey);
        //通过过滤相反性别,屏蔽id集合,不喜欢列表,已经听过的语音  随机查询出一个语音
        Sound sound = soundApi.findSound(gender, noFindIds, notLikeUserIds, readVoiceIds);
        if (sound == null){
            throw new TanHuaException("获取语音失败，请稍后再试");
        }
        //将语音id保存到已读列表
        redisTemplate.opsForSet().add(readVoiceKey, sound.getId());

/*
        //根据用户性别查询出所有语音
        List<Sound> sounds = soundApi.findAllId(findGender);
        //获得所有语音的userId
        List<ObjectId> objectIds = sounds.stream().map(Sound::getId).collect(Collectors.toList());
        log.debug("collect:{}" + objectIds.toString());
        //将list集合转成数组格式 取随机
        ObjectId[] ids = objectIds.toArray(new ObjectId[0]);
        //获得用户 已经听过的语音的 objectId
        String readVoiceKey = Constants.readVoice_USER_KEY + userId;
        Set readVoiceSet = redisTemplate.opsForSet().members(readVoiceKey);
        //循环过滤条件
        Sound sound = new Sound();
        boolean flag = true;
        //如果 用户听过的语音的列表为空  不需要添加过滤条件
        if (readVoiceSet == null) {
            while (flag) {
                //先随机产生一个下标再获取元素
                int index = (int) (Math.random() * ids.length);
                ObjectId id = ids[index];
                Sound sound1 = soundApi.findVoice(id);
                if (!noFindIds.contains(sound1.getUserId())) {
                    sound = sound1;
                    flag = false;
                }
            }
            redisTemplate.opsForSet().add(readVoiceKey, sound.getId().toString());
        } else {
            while (flag) {
                //先随机产生一个下标再获取元素
                int index = (int) (Math.random() * ids.length);
                ObjectId id = ids[index];
                Sound sound1 = soundApi.findVoice(id);
                if (!noFindIds.contains(sound1.getUserId()) && !readVoiceSet.contains(sound1.getId().toString())) {
                    sound = sound1;
                    flag = false;
                }
            }
            //将语音保存到已读列表
            redisTemplate.opsForSet().add(readVoiceKey, sound.getId());
        }

 */
        return sound;
    }
}
